package com.vavr.checkincms.common.util;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.Optional;
import java.util.function.Supplier;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import static ch.qos.logback.core.encoder.ByteArrayUtil.hexStringToByteArray;

import cn.hutool.core.text.CharSequenceUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

/**
 * @since 2.0.0
 */
@Slf4j
public class SessionUtil {
    public static Optional<String> existCookieValue(HttpServletRequest request, String cookieName) {
        return Optional.ofNullable(request.getCookies())
                .map(Arrays::asList)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(cookie -> cookieName.equals(cookie.getName()))
                .findFirst()
                .map(Cookie::getValue);
    }

    public static void saveSseEmitter(SseEmitter sseEmitter, String cookieValue) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        requestAttributes.setAttribute(cookieValue, sseEmitter, RequestAttributes.SCOPE_SESSION);
    }

    /**
     * 从会话作用域中找key对应的值，如果缺失就创建，并塞回会话作用域中。
     *
     * @param key 键
     * @param supplier 创建者
     * @param <T>
     * @return
     */
    public static <T> T createIfAbsent(String key, Supplier<T> supplier) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        return Optional.ofNullable(requestAttributes.getAttribute(key, RequestAttributes.SCOPE_SESSION))
                .map(t -> (T) t)
                .orElseGet(() -> {
                    T t = supplier.get();
                    requestAttributes.setAttribute(key, t, RequestAttributes.SCOPE_SESSION);
                    return t;
                });
    }

    public static void completeAndRemSseEmitter(String cookieValue) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        Optional.ofNullable((SseEmitter) requestAttributes.getAttribute(cookieValue, RequestAttributes.SCOPE_SESSION))
                .ifPresent(t -> {
                    try {
                        t.complete();
                    }
                    catch (NullPointerException e) {
                        // 场景：后端收到on_stream_end，已主动flush，前端收到后端终止事件也会主动触发后端flush，这个时候流已经刷过了。
                        log.debug("重复flush，可忽略");
                    }
                    requestAttributes.removeAttribute(cookieValue, RequestAttributes.SCOPE_SESSION);
                });
    }

    public static String encodeNgxUid(String uid) {
        try {
            String realUid = "";
            boolean contains = uid.contains("=");
            if (contains) {
                realUid = CharSequenceUtil.subAfter(uid, "=", false);
            }
            byte[] uidBytes = hexStringToByteArray(realUid);
            ByteBuffer buffer = ByteBuffer.wrap(uidBytes);
            int[] hostByteOrderInts = new int[4];
            for (int i = 0; i < 4; i++) {
                hostByteOrderInts[i] = Integer.reverseBytes(buffer.getInt());
            }
            ByteBuffer resultBuffer = ByteBuffer.allocate(16);
            for (int i = 0; i < 4; i++) {
                resultBuffer.putInt(hostByteOrderInts[i]);
            }
            byte[] binaryCookie = resultBuffer.array();
            String encodedUid = Base64.getEncoder().encodeToString(binaryCookie);
            return contains ? (CharSequenceUtil.subBefore(uid, "=", false) + "=" + encodedUid) : encodedUid;
        }
        catch (Exception e) {
            log.error("nginx uid = {} 编码异常！", uid);
            return CharSequenceUtil.EMPTY;
        }
    }
}
